package com.platform.rbac.aop;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.platform.rbac.annotation.Logical;
import com.platform.rbac.annotation.RequiresPermissions;
import com.platform.rbac.annotation.RequiresRoles;
import com.platform.rbac.annotation.TokenCheck;
import com.platform.rbac.callback.RoleAndPermissionCallback;
import com.platform.rbac.exception.PermissionFailException;
import com.platform.rbac.exception.RoleFailException;
import com.platform.rbac.exception.TokenAutheFailException;
import com.platform.rbac.jwt.JWTUtil;
import com.platform.rbac.spring.SpringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.BeansException;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.List;

@Component
@Aspect
public class RBACAop {


    RoleAndPermissionCallback roleAndPermissionCallback;

    @Pointcut("@annotation(com.platform.rbac.annotation.RequiresPermissions) " +
            "|| @annotation(com.platform.rbac.annotation.RequiresRoles)"+
            "|| @annotation(com.platform.rbac.annotation.TokenCheck)")
    public void point() {

    }

    @Around("point()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("身份和权限检查");
        Object res = null;
        LocalDateTime start = LocalDateTime.now();
        // 接收到请求，记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();


        String username = checkToken(request, false);

        //检查是否有注解
        //从切面织入点处通过反射机制获取织入点处的方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取切入点所在的方法
        Method method = signature.getMethod();

        RequiresPermissions requiresPermissions = method.getAnnotation(RequiresPermissions.class);
        RequiresRoles requiresRoles = method.getAnnotation(RequiresRoles.class);
        TokenCheck tokenCheck = method.getAnnotation(TokenCheck.class);
        if (requiresPermissions != null || requiresRoles != null || tokenCheck!=null) {
            //身份验证
            if (tokenCheck!=null){
                checkToken(request, true);
            }


            try {
                roleAndPermissionCallback = SpringUtils.getBean(RoleAndPermissionCallback.class);
            } catch (BeansException e) {
                System.err.println("please implements com.platform.rbac.callback.RoleAndPermissionCallback ");
            }
            if (roleAndPermissionCallback == null) {
                throw new RuntimeException("please implements com.platform.rbac.callback.RoleAndPermissionCallback ");
            }

            if (requiresRoles != null) {
                checkRoles(username, requiresRoles);
                res = joinPoint.proceed();
                return res;
            }


            if (requiresPermissions != null) {
                checkPermission(username, requiresPermissions);
            }
        }
        res = joinPoint.proceed();
        return res;

    }

    /**
     * 检查权限
     * @param username
     * @param requiresPermissions
     */
    private void checkPermission(String username, RequiresPermissions requiresPermissions) {
        //校验是有相应的权限
        List<String> permissionsList = roleAndPermissionCallback.loadPerms(username);
        //获取注解上的权限集合
        String[] permissions = requiresPermissions.value();
        boolean hasPermission=true;
        if (requiresPermissions.logical().equals(Logical.AND)){
            for (String per : permissions) {
                if (!permissionsList.contains(per)){
                    hasPermission=false;
                    break;
                }
            }
        }else {
            for (String role : permissions) {
                if (permissionsList.contains(role)){
                    hasPermission=true;
                    break;
                }
            }
        }
        if (!hasPermission){
            throw new PermissionFailException("权限不足");
        }
    }

    /**
     * 检查角色
     * @param username
     * @param requiresRoles
     */
    private void checkRoles(String username, RequiresRoles requiresRoles) {
        //校验是否有相应的角色
        List<String> roleList = roleAndPermissionCallback.loadRoles(username);
        //获取注解上的角色集合
        String[] roles = requiresRoles.value();

        if (requiresRoles.logical().equals(Logical.AND)){
            for (String role : roles) {
                if (!roleList.contains(role)){
                    throw new RoleFailException("您没有["+role+"]角色");
                }
            }
        }else {//OR
            boolean hasRole=false;
            for (String role : roles) {
                if (roleList.contains(role)){
                    hasRole=true;
                    break;
                }
            }
            if (!hasRole){
                throw new RoleFailException("您没有["+ ArrayUtil.join(roles,",") +"]角色");
            }
        }
    }


    /**
     *  header中如果存在 token 则进行token验证
     * @param request
     * @param required
     * @return
     */
    private String checkToken(HttpServletRequest request, boolean required) {
        String token = request.getHeader("token");
        if (required){
            if (StrUtil.isEmpty(token)){
                throw new TokenAutheFailException("身份验证失败，请重新登录！");
            }
            //验证token是否有效
            String username = JWTUtil.getUsername(token);
            if (username == null || !JWTUtil.verify(token, username)) {
                throw new TokenAutheFailException("身份验证失败，请重新登录！");
            }
            return username;
        }else {
            if (StrUtil.isNotEmpty(token)) {
                //验证token是否有效
                String username = JWTUtil.getUsername(token);
                if (username == null || !JWTUtil.verify(token, username)) {
                    throw new TokenAutheFailException("身份验证失败，请重新登录！");
                }
                return username;
            }
            return "";
        }


    }



}
